/*******************************************************************************
 * Copyright (c) baifeiq.
 * 
 * @date            @author         @brief
 * 2021-12-30       baifeiq         First version
 */

#ifndef	OS_LIST_H__
#define OS_LIST_H__
#include "os_define.h"

/**
 * os_container_of - Return the member address of ptr, if the type of ptr is the
 * struct type.
 */
#define os_container_of(ptr, type, member) \
    ((type *)((unsigned long)(ptr) - (unsigned long)(&((type *)0)->member)))

/**
 * @brief Get the struct for this entry
 * 
 * @param node The entry point
 * @param type The type of structure
 * @param member The name of list in structure
 */
#define os_list_entry(node, type, member) \
    os_container_of(node, type, member)


/**
 * @brief Initialize a list
 *
 * @param l List to be initialized
 */
os_inline void os_list_init(os_list_t *l)
{
    l->next = l->prev = l;
}

/**
 * @brief Insert a node before a list
 *
 * @param n New node to be inserted
 * @param l List to insert it
 */
os_inline void os_list_insert_before(os_list_t *l, os_list_t *n)
{
    l->prev->next = n;
    n->prev = l->prev;

    l->prev = n;
    n->next = l;
}

/**
 * @brief Insert a node after a list
 *
 * @param l List to insert it
 * @param n New node to be inserted
 */
os_inline void os_list_insert_after(os_list_t *l, os_list_t *n)
{
    l->next->prev = n;
    n->next = l->next;

    l->next = n;
    n->prev = l;
}

/**
 * @brief Remove node from list.
 * 
 * @param n The node to remove from the list.
 */
os_inline void os_list_remove(os_list_t *n)
{
    n->next->prev = n->prev;
    n->prev->next = n->next;

    n->next = n->prev = n;
}

/**
 * @brief Cut and remove the last node of the list.
 *
 * @param l List of removed nodes.
 */
os_inline os_list_t *os_list_out_last(os_list_t *l)
{
    os_list_t *n = l->prev;

    n->next->prev = n->prev;
    n->prev->next = n->next;

    n->next = n->prev = n;

    return n;
}

/**
 * @brief Cut and take out the first node of the list.
 *
 * @param l List of removed nodes.
 */
os_inline os_list_t *os_list_out_first(os_list_t *l)
{
    os_list_t *n = l->next;

    n->next->prev = n->prev;
    n->prev->next = n->next;

    n->next = n->prev = n;

    return n;
}

/**
 * @brief Tests whether a list is empty
 * 
 * @param l The list to test.
 */
os_inline os_int32_t os_list_isempty(const os_list_t *l)
{
    return l->next == l;
}

/**
 * @brief Get the list length
 * 
 * @param l The list to get.
 */
os_inline os_uint32_t os_list_len(const os_list_t *l)
{
    os_uint32_t len = 0;
    const os_list_t *p = l;
    while (p->next != l)
    {
        p = p->next;
        len ++;
    }

    return len;
}

#endif  //OS_LIST_H__